package cyb;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * 亚频繁模式
 * <p>
 * 1.二阶模式：
 * 候选模式及星型参与实例 通过 星型邻居 得到
 * 通过支持度剪枝
 * <p>
 * 2.n+1阶模式:
 * 候选模式 通过 n阶模式前n-1项相同组合 得到
 * 星型参与实例 通过 n阶参与实例和2阶参与实例 得到
 * 通过支持度剪枝
 * <p>
 * 模式按字母顺序，k-core和出现次数降序
 */
public class SPColocation {
    public static Map<String, Integer> countPerFeature = new HashMap<>();
    public static String spNeighborPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\" + Properties.dataFrom + "_" + Properties.k + "hop_sp_neighbor_"
            + Properties.maxNum + "_" + Properties.R + "_" + Properties.support + ".txt";
    public static String spReverseNeighborPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\" + Properties.dataFrom + "_" + Properties.k + "spReverse_neighbor_"
            + Properties.maxNum + "_" + Properties.R + "_" + Properties.support + ".txt"; //二阶模式
    public static String khopNeighborPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\" + Properties.dataFrom + "_" + Properties.k + "hop_neighbor_"
            + Properties.maxNum + "_" + Properties.R + "_" + Properties.support + ".txt";


    public static void getSpNeighbors(edu.wlu.cs.levy.CG.KDTree<String> oldKdTree, Map<String, POI> poiMap) throws Exception {
        Long start = System.currentTimeMillis();
        int num = 1;
        //B:{A:a1 ; B:b1}
        System.out.println("===================================通过" + Properties.dataFrom + "数据，挖掘模式！");
        if (!FileUtil.exist(CoLocation.neighborPath)) {
            if (Properties.dataFrom.equals("make")) {
                Util.getOldNeighborsByMakeData(oldKdTree, poiMap);
            } else {
                CoLocation.getOldNeighbors(oldKdTree, poiMap);
            }
        }
        String neighborPath = CoLocation.neighborPath;
        if (Properties.k > 1) {
            Util.getKhopNeighbor();
            neighborPath = khopNeighborPath;
            System.out.println("=>  读取" + Properties.k + "跳邻居集！耗时：" + (System.currentTimeMillis() - start) + "毫秒");
        }
        start = System.currentTimeMillis();
        Map<String, List<Set<String>>> twoCoMap2 = new HashMap<>();
        try (
                BufferedWriter spBw = new BufferedWriter(new FileWriter(spNeighborPath));
                BufferedWriter spReverseBw = new BufferedWriter(new FileWriter(spReverseNeighborPath));
        ) {

            List<String> neighborStrList = Files.readAllLines(Paths.get(neighborPath));

            for (String neighborStr : neighborStrList) {
                String starId = neighborStr.split(":")[0];
                POI star = poiMap.get(starId);
                String starType = star.getType();
                String[] neighborArr = neighborStr.split(":")[1].split(",");
                Set<String> starNeighborTypeSet = new TreeSet(new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
                        return Integer.valueOf(o1).compareTo(Integer.valueOf(o2));
                    }
                });

                for (String neighborId : neighborArr) {
                    POI neighbor = poiMap.get(neighborId);
                    String neighborType = neighbor.getType();
                    if (starId.equals("6844")) {
//                        System.out.println(1);
                    }
                    if (starId.equals("8915") && neighbor.getId().equals("13049")) {
//                        System.out.println(1);
                    }
                    //排除同特征poi
                    if (!neighborType.equals(starType)) {
                        if ((starType.equals("308") && neighborType.equals("577")) || (starType.equals("577") && neighborType.equals("308"))) {
                            System.out.println(1);
                        }
                        starNeighborTypeSet.add(neighbor.getType());
                    }
                }

                //去除不含邻居集的点
                if (starNeighborTypeSet.size() != 0) {
                    String starNeighborType = starNeighborTypeSet.stream().collect(Collectors.joining(","));

                    spBw.write(starId + ":" + starNeighborType);
                    spBw.newLine();

                    for (String neighborType : starNeighborTypeSet) {
                        String twoCoType;
                        int index = 0;
                        if (Integer.parseInt(starType) < Integer.parseInt(neighborType)) {
                            twoCoType = starType + "," + neighborType;
                        } else {
                            twoCoType = neighborType + "," + starType;
                            index = 1;
                        }
                        if ((starType.equals("308") && neighborType.equals("577")) || (starType.equals("577") && neighborType.equals("308"))) {
                            System.out.println(1);
                        }
                        List<Set<String>> setList = twoCoMap2.get(twoCoType);
                        if (setList == null) {
                            setList = new ArrayList<>(2);
                            setList.add(new HashSet<>());
                            setList.add(new HashSet<>());
                            twoCoMap2.put(twoCoType, setList);
                        }
                        setList.get(index).add(starId);
                    }
                }
            }

            for (Map.Entry<String, List<Set<String>>> entry : twoCoMap2.entrySet()) {
                String twoCoType = entry.getKey();
                List<Set<String>> instanceList = entry.getValue();
                StringBuffer sb = new StringBuffer();
                sb.append(twoCoType + ":");
                sb.append(instanceList.get(0).stream().collect(Collectors.joining(",")));
                sb.append(";");
                sb.append(instanceList.get(1).stream().collect(Collectors.joining(",")));
                spReverseBw.write(sb.toString());
                spReverseBw.newLine();
            }

        } catch (Exception e) {
            System.out.println("搜索k跳邻居 数据异常！！！");
            e.printStackTrace();
        }
        System.out.println("========4.星型特征，倒排邻居集获取完成，耗时: " + (System.currentTimeMillis() - start) + "毫秒");
    }

    public static String getCoDetail(Map<String, List<String>> nCoMap, int n) {
        int instanceNum = 0;
        for (List<String> list : nCoMap.values()) {
            instanceNum += list.size();
        }
        return (" => " + n + "阶模式数：" + nCoMap.size() + " ,总实例数：" + instanceNum);
    }

    public static Map<String, List<String>> gen1Co(Map<String, String> poiMap) {
        Map<String, List<String>> oneCoMap = new HashMap<>();
        for (Map.Entry<String, String> entry : poiMap.entrySet()) {
            String id = entry.getKey();
            String type = entry.getValue();

            List<String> poiList = oneCoMap.computeIfAbsent(type, k -> new LinkedList<>());
            poiList.add(id);
        }
        for (Map.Entry<String, List<String>> entry : oneCoMap.entrySet()) {
            countPerFeature.put(entry.getKey(), entry.getValue().size());
        }
        return oneCoMap;
    }

    public static Map<String, List<Set<String>>> gen2CoBySpFile(Map<String, String> poiTypeMap, Map<String, List<String>> oneCoMap) throws IOException {
        long start = System.currentTimeMillis();

        Map<String, Set<String>> neighborTypeMap = new HashMap<>();
        List<String> neighborStrList = Files.readAllLines(Paths.get(spNeighborPath));

        for (String str : neighborStrList) {
            String[] split = str.split(":");
            String starId = split[0];
            if (split.length > 1) {
                Set<String> neighborTypeSet = Stream.of(split[1].split(",")).collect(Collectors.toSet());
                neighborTypeMap.put(starId, neighborTypeSet);
            }
        }

        Map<String, List<Set<String>>> twoCoMap = new HashMap<>();
        for (Map.Entry<String, List<String>> starTypeEntry : oneCoMap.entrySet()) {
            String starType = starTypeEntry.getKey(); // 类似 A
            List<String> starTypePoiList = starTypeEntry.getValue();
            for (String starId : starTypePoiList) { // 类似 a1 a2 a3 a4
                Set<String> neighborTypeSet = neighborTypeMap.get(starId);
                if (neighborTypeSet == null) {
                    continue;
                }
                //每个点的邻居特征集 如（a1: B,C,E）
                for (String neighborType : neighborTypeSet) {
                    String twoCoTypeStr;
                    Integer index = 0;
                    if (Integer.parseInt(starType) < Integer.parseInt(neighborType)) {
                        twoCoTypeStr = starType + "," + neighborType;
                    } else {
                        twoCoTypeStr = neighborType + "," + starType;
                        index = 1;
                    }

                    List<Set<String>> starInstancesList = twoCoMap.get(twoCoTypeStr);
                    if (starInstancesList == null) {
                        Set<String> stars1 = new HashSet<>();
                        Set<String> stars2 = new HashSet<>();
                        starInstancesList = new ArrayList<>(2);
                        starInstancesList.add(stars1);
                        starInstancesList.add(stars2);
                        twoCoMap.put(twoCoTypeStr, starInstancesList);
                    }
                    starInstancesList.get(index).add(starId);
                }
            }
        }
        System.out.println("星型邻居生成2阶模式耗时：" + (System.currentTimeMillis() - start) + "毫秒");
        return twoCoMap;
    }

    /**
     * 直接从倒排邻居集生成2阶模式
     * 比如A,B 找A的B类星型中心集合和 找B的A类星型中心集合
     * TODO 1，将不符合的从倒排邻居集删除 2，将2阶模式剪枝操作移到这里
     */
    public static Map<String, List<Set<String>>> gen2CoByInvertSpFile() throws IOException {
        long start = System.currentTimeMillis();
        Map<String, List<Set<String>>> twoCoMap2 = new HashMap<>();
        List<String> allLines = Files.readAllLines(Paths.get(spReverseNeighborPath));

        for (String line : allLines) {
            String[] split = line.split(":");
            String twoCoType = split[0];
            String[] instanceStr = split[1].split(";");
            if (instanceStr.length<2){
                //TODO 处理
                continue;
            }

            List<Set<String>> instanceList = new ArrayList<>(2);

            Set<String> instance1 = Stream.of(instanceStr[0].split(",")).collect(Collectors.toSet());
            instanceList.add(instance1);
            Set<String> instance2 = Stream.of(instanceStr[1].split(",")).collect(Collectors.toSet());
            instanceList.add(instance2);

            twoCoMap2.put(twoCoType, instanceList);
        }
        System.out.println("倒排邻居生成2阶模式耗时：" + (System.currentTimeMillis() - start) + "毫秒");
        return twoCoMap2;
    }

    public static String getSPCoDetail(Map<String, List<Set<String>>> nCoMap, int n) {
        int instanceNum = 0;
        for (List<Set<String>> listtt : nCoMap.values()) {
            for (Set<String> list : listtt) {
                instanceNum += list.size();
            }
        }
        return (" => " + n + "阶模式数：" + nCoMap.size() + " ,总实例数：" + instanceNum);
    }

    /**
     * 将n阶模式的地点代码，转成中文地点
     */
    private static String code2name(String nCoStr) {
        if (nCoStr.startsWith("[")) {
            nCoStr = nCoStr.replace("[", "");
            nCoStr = nCoStr.replace("]", "");
        }
        Map<String, String> typeCode = getTypeCode();
        StringBuffer sb = new StringBuffer();
        for (String code : nCoStr.split(",")) {
            String name = typeCode.get(code);
            sb.append(name + ",");
        }
        return "[" + sb.toString() + "]";
    }

    private static Map<String, String> getTypeCode() {
        Map<String, String> typeCode = new HashMap<>();
        List<String> poiTypeList = null;
        try {
            poiTypeList = Files.readAllLines(Paths.get(CoLocation.poiTypePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (String str : poiTypeList) {
            String[] split = str.split(",");
            typeCode.put(split[0], split[1]);
        }
        return typeCode;
    }

    /**
     * 获取每个实例的星型邻居类型 如(a1: B,E,F)
     */
//    public static Map<String, Set<String>> getNeighborTypesFromFile(Map<String, String> poiMap) throws IOException {
//        Map<String, Set<String>> neighborMap = new HashMap<>();
//        List<String> neighborStrList = Files.readAllLines(Paths.get(CoLocation.neighborPath));
//
//        for (String str : neighborStrList) {
//            String[] split = str.split(":");
//            String starId = split[0];
//            if (split.length > 1) {
//                Set<String> neighborTypeSet = new TreeSet<>();
//                for (String neighborId : split[1].split(",")) {
//                    String neighborType = poiMap.get(neighborId);
//                    neighborTypeSet.add(neighborType);
//                }
//                neighborMap.put(starId, neighborTypeSet);
//            } else { // 将没有邻居的poi点筛除
//                poiMap.remove(starId);
//            }
//        }
//        return neighborMap;
//    }


    /**
     * 获取每个实例的星型邻居,边重复存储2次 如(a1: {B:{b1,b2}, E, F})
     */
//    public static Map<String, Map<String, Set<String>>> getNeighborsFromFile(Map<String, String> poiMap) throws IOException {
//        Map<String, Map<String, Set<String>>> neighborMap = new HashMap<>();
//        List<String> neighborStrList = Files.readAllLines(Paths.get(CoLocation.neighborPath));
//
//        for (String str : neighborStrList) {
//            String[] split = str.split(":");
//            String starId = split[0];
//            if (split.length > 1) {
//                Map<String, Set<String>> neighborTypeMap = new HashMap<>();
//                for (String neighborId : split[1].split(",")) {
//                    String neighborType = poiMap.get(neighborId);
//
//                    Set<String> neighborSet = neighborTypeMap.get(neighborType);
//                    if (neighborSet == null) {
//                        neighborSet = new HashSet<>();
//                    }
//                    neighborSet.add(neighborId);
//                    neighborTypeMap.put(neighborType, neighborSet);
//                }
//                if (neighborTypeMap.size() != 0) {
//                    neighborMap.put(starId, neighborTypeMap);
//                }
//            } else {// 将没有邻居的poi点筛除
//                poiMap.remove(starId);
//            }
//        }
//        return neighborMap;
//    }
    public static Map<String, Map<String, Set<String>>> getInvertedNeighborMap(Map<String, Set<String>> neighborMap, Map<String, String> poiMap) {
        Map<String, Map<String, Set<String>>> invertedNeighborMap = new HashMap<>();
        for (Map.Entry<String, Set<String>> entry : neighborMap.entrySet()) {
            String starId = entry.getKey(); //a1
            String starType = poiMap.get(starId);

            Set<String> value = entry.getValue(); //B,C,D
            for (String neighborType : value) {
                Map<String, Set<String>> map = invertedNeighborMap.computeIfAbsent(neighborType, k -> new HashMap<>());
                Set<String> starList = map.computeIfAbsent(starType, k -> new HashSet<>());
                starList.add(starId);
            }
        }
        return invertedNeighborMap;
    }

    /**
     * 获取每个实例的星型邻居类型 如(a1: B,E,F)
     */
    public static Map<String, Set<String>> getNeighborTypesFromSpFile(Map<String, String> poiMap) throws IOException {
        Map<String, Set<String>> neighborMap = new HashMap<>();
        List<String> neighborStrList = Files.readAllLines(Paths.get(spNeighborPath));

        for (String str : neighborStrList) {
            String[] split = str.split(":");
            String starId = split[0];
            if (split.length > 1) {
                Set<String> neighborTypeSet = Stream.of(split[1].split(",")).collect(Collectors.toSet());
                neighborMap.put(starId, neighborTypeSet);
            } else { // 将没有邻居的poi点筛除
                poiMap.remove(starId);
            }
        }
        return neighborMap;
    }

    public static Map<String, Map<String, Set<String>>> getInvertedNeighborTypesFromSpFile(Map<String, String> poiMap) throws IOException {
        List<String> neighborStrList = Files.readAllLines(Paths.get(spNeighborPath));
        Map<String, Map<String, Set<String>>> invertedNeighborMap = new HashMap<>();

        for (String str : neighborStrList) {
            String[] split = str.split(":");
            String starId = split[0];
            String starType = poiMap.get(starId);
            if (split.length > 1) {
                Set<String> neighborTypeSet = Stream.of(split[1].split(",")).collect(Collectors.toSet()); // B,C,D
                for (String neighborType : neighborTypeSet) {
                    Map<String, Set<String>> map = invertedNeighborMap.computeIfAbsent(neighborType, k -> new HashMap<>());
                    Set<String> starList = map.computeIfAbsent(starType, k -> new HashSet<>());
                    starList.add(starId);
                }
            } else { // 将没有邻居的poi点筛除
                poiMap.remove(starId);
            }
        }
        return invertedNeighborMap;
    }

    public static void cut(Map<String, List<Set<String>>> nCoMap) {
        List<String> removeCoList = new LinkedList<>();
        int jie = 0;
        for (Map.Entry<String, List<Set<String>>> nCoEntry : nCoMap.entrySet()) {
            String nCoType = nCoEntry.getKey();
            String[] nCoTypeArr = nCoType.split(","); //如 (A,B,D)
            List<Set<String>> starInstanceList = nCoEntry.getValue();  // 如（a1,a2 b2,b3 d5,d1）
            if (jie == 0) jie = nCoTypeArr.length;
            double minCoSupport = 2;
            //计算n阶模式的每个特征的参与度，进而计算模式的支持度
            for (int i = 0; i < nCoTypeArr.length; i++) {
                String cotype = nCoTypeArr[i];

                double tempSupport = starInstanceList.get(i).size() / Util.int2double(countPerFeature.get(cotype));
                if (tempSupport < minCoSupport) {
                    minCoSupport = tempSupport;
                }
            }
            if (minCoSupport < Properties.support) {
                removeCoList.add(nCoType);
            }
        }
        for (String removeCo : removeCoList) {
            nCoMap.remove(removeCo);
        }
        System.out.println("剪枝后：=> " + jie + "阶模式剪枝 " + removeCoList.size() + " 个");

    }

    /**
     * 将n-1阶的星型参与实例取交集，为n+1阶模式的前n—1阶星型参与实例
     * 末尾2项通过查询 2阶模式星型参与实例获取
     */
    public static Map<String, List<List<String>>> genAllNPlusCo(Map<String, List<List<String>>> sp_nCoMap,
                                                                Map<String, List<String>> oneCoMap, Map<String, Set<String>> neighborMap,
                                                                Map<String, Map<String, Set<String>>> invertedNeighborMap, String nCoMethod) {

        int jie = 0;
        String method = nCoMethod;
        Map<String, List<List<String>>> nPlusCoMap = new HashMap<>();
        for (String starTypeStr : sp_nCoMap.keySet()) {
            if (jie == 0) jie = starTypeStr.split(",").length;
            int index1 = starTypeStr.lastIndexOf(",");
            String pre1 = starTypeStr.substring(0, index1), suf1 = starTypeStr.substring(index1 + 1);
            for (String neighborTypeStr : sp_nCoMap.keySet()) {
                int index2 = neighborTypeStr.lastIndexOf(",");
                String pre2 = neighborTypeStr.substring(0, index2), suf2 = neighborTypeStr.substring(index2 + 1);
                String suf = suf1 + "," + suf2;
                String nPlusCoType = starTypeStr + "," + suf2;
                if (pre1.equals(pre2) && Integer.valueOf(suf1) < Integer.valueOf(suf2)) { //前n-1阶相同，最后一个特征 左边比右边小
//                    if (twoCoMap.get(suf) == null) {
//                        continue; //末尾2阶模式不成立，则n+1阶模式不成立
//                    }
                    //根据n阶模式实例得到n+1阶，  会使用2阶模式和n阶模式（占用内存多）,要交集还要将n阶模式实例集设成set，更占内存
//                    List<Set<String>> starInstanceListRS = new ArrayList<>(jie + 1);
//                    if (method.equals("base") || method.equals("both")) {
//                        List<Set<String>> starInstanceList1 = sp_nCoMap.get(starTypeStr);
//                        List<Set<String>> starInstanceList2 = sp_nCoMap.get(neighborTypeStr);
//
//
//                        for (int i = 0; i < jie + 1; i++) { //将n-1阶的星型参与实例取交集，为n+1阶模式的前n—1阶星型参与实例
//                            Set<String> collect1 = new HashSet<>();
//                            Set<String> collect2 = new HashSet<>();
//                            if (i < jie - 1) {
//                                collect1 = starInstanceList1.get(i);
//                                collect2 = starInstanceList2.get(i);
//                            }
//                            //末尾2项通过查询 2阶模式星型参与实例获取
//                            if (i == jie - 1) {
//                                collect1 = starInstanceList1.get(jie - 1);
//                                collect2 = twoCoMap.get(suf).get(0);
//                            }
//                            if (i == jie) {
//                                collect1 = starInstanceList2.get(jie - 1);
//                                collect2 = twoCoMap.get(suf).get(1);
//                            }
//                            Set<String> result = new HashSet<>();
//                            result.addAll(collect1);
//                            result.retainAll(collect2);
//                            starInstanceListRS.add(result);
//                        }
//                        if (method.equals("base")) {
//                            nPlusCoMap.put(nPlusCoType, starInstanceListRS);
//                        }
//                    }


                    //根据星型实例得到n+1阶实例，  会使用1阶模式，和邻居集 (less最慢)
                    List<List<String>> starInstanceListRS2 = new ArrayList<>(jie + 1);
                    if (method.equals("less") || method.equals("both")) {
                        String[] nPlusCoTypeArr = nPlusCoType.split(",");
                        for (int i = 0; i < nPlusCoTypeArr.length; i++) {
                            List<String> starList = oneCoMap.get(nPlusCoTypeArr[i]);
                            List<String> starInstanceList = new LinkedList<>();
                            for (String starId : starList) {//找第一个特征的每个实例的 星型实例
                                Set<String> neighborTypeSet = neighborMap.get(starId);
                                Set<String> nPlusCoTypeSet = new HashSet<>(Arrays.asList(nPlusCoTypeArr));
                                nPlusCoTypeSet.remove(nPlusCoTypeArr[i]);
                                boolean containsAll = neighborTypeSet.containsAll(nPlusCoTypeSet);
                                if (containsAll) {
                                    starInstanceList.add(starId);
                                }
                            }
                            starInstanceListRS2.add(starInstanceList);
                        }
                        if (method.equals("less")) {
                            nPlusCoMap.put(nPlusCoType, starInstanceListRS2);
                        }
                    }

                    //根据倒排星型邻居,     会使用倒排邻居集
                    List<List<String>> starInstanceListRS3 = new ArrayList<>(jie + 1);
                    if (method.equals("invert") || method.equals("both")) {
                        boolean canSP = genNPlusCo(method, nPlusCoType, nPlusCoMap, oneCoMap, invertedNeighborMap, starInstanceListRS3);
                        if (!canSP) {
                            //某个特征支持度不够，直接跳过该模式
                            for (String type : nPlusCoType.split(",")) {
                                starInstanceListRS3.add(new LinkedList<>());
                            }
                        }
                    }

                    if (method.equals("both")) {
                        for (int i = 0; i < jie + 1; i++) {
//                            Set<String> list = starInstanceListRS.get(i);
                            List<String> list2 = starInstanceListRS2.get(i);
                            List<String> list3 = starInstanceListRS3.get(i);
                            if (!(list2.size() == list3.size())) {
                                System.out.println("2个方法的到的亚频繁模式不一致！！！");
                                return null;
                            }
                        }
                        nPlusCoMap.put(nPlusCoType, starInstanceListRS3);
                    }
                }
            }
        }

        return nPlusCoMap;
    }

    public static boolean genNPlusCo(String method, String nPlusCoType, Map<String, List<List<String>>> nPlusCoMap,
                                     Map<String, List<String>> oneCoMap,
                                     Map<String, Map<String, Set<String>>> invertedNeighborMap,
                                     List<List<String>> starInstanceListRS3) {
        Map<String, Set<String>> rs = new HashMap<>();
        boolean canSP = true;
        for (String type : nPlusCoType.split(",")) {
            Map<String, Set<String>> starMap = invertedNeighborMap.get(type);
            for (String starType : nPlusCoType.split(",")) {
                if (!type.equals(starType)) { //邻居不存本特征
                    Set<String> starList = starMap.get(starType);
                    Double support = Double.valueOf(0);
                    if (oneCoMap.get(starType) != null && starList != null) { //该点有邻居集
                        support = Util.int2double(starList.size()) / oneCoMap.get(starType).size();
                    }
                    if (support < Properties.support) {
                        return false;
                    }
                    Set<String> list = rs.get(starType);
                    Set<String> result = new HashSet<>();
                    if (list != null) {
                        result.addAll(list);
                        result.retainAll(starList);
                    } else {
                        result = starList;
                    }
                    rs.put(starType, result);
                }
            }
        }
        for (String type : nPlusCoType.split(",")) {
            starInstanceListRS3.add(new ArrayList<>(rs.get(type)));
        }
        if (method.equals("invert")) {
            nPlusCoMap.put(nPlusCoType, starInstanceListRS3);
        }
        return true;
    }


    public static Map<String, String> preprocess() throws Exception {
        Map<String, POI> poiMap = new HashMap<>();
        //1.处理excel转成txt
        CoLocation.excel2txt(CoLocation.poiExcelPath, CoLocation.poiTxtPath);
        //2.对坐标去重，地点类型编码
        CoLocation.txtProceed(CoLocation.poiTxtPath);

        if (!FileUtil.exist(spNeighborPath) || !FileUtil.exist(spReverseNeighborPath)) {
            //2.1.建KD树，共20多万个点，测试选用前1万个
            edu.wlu.cs.levy.CG.KDTree<String> old_KdTree = null;
            if (Properties.dataFrom.equals("make")) {
                old_KdTree = Util.buildOldKDTreeByMakeData(poiMap);
            } else {
                old_KdTree = CoLocation.buildOldKDTree
                        (Files.readAllLines(Paths.get(CoLocation.poiTxtFinalPath)), poiMap, Properties.maxNum);
            }
            //2.2 获取邻居特征集，和邻居集，分别保存到2个txt(不包含空邻居的点)
            getSpNeighbors(old_KdTree, poiMap);
        } else {
            if (Properties.dataFrom.equals("make")) {
                poiMap = CoLocation.getPOIFromFile(Properties.maxNum);
            } else {
                poiMap = CoLocation.getPOIFromFile(Properties.maxNum);

            }
        }
        Map<String, String> rs = new HashMap<>();
        poiMap.forEach((key, value) -> rs.put(key, value.getType()));
        return rs;
    }

    /**
     * 分区1：通过频繁2阶模式生成 所有候选长模式
     *
     * @param twoCoTypes     频繁2阶模式
     * @param twoCoreFeature 可能组成3阶模式的特征
     */
    public static List<String> partition1(Set<String> twoCoTypes, Set<String> twoCoreFeature, boolean consider2core, Set<String> oneCoTypes) {
        Map<String, List<String>> partition1Map = new TreeMap<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return Integer.valueOf(o1).compareTo(Integer.valueOf(o2));
            }
        });

        for (String twoCoType : twoCoTypes) {
            String first = twoCoType.split(",")[0];
            String second = twoCoType.split(",")[1];
            if (consider2core) {
                if (!(twoCoreFeature.contains(first) && twoCoreFeature.contains(second))) {
                    //只考虑可能组成3阶模式的特征
                    continue;
                }
            }
            List<String> l1 = partition1Map.get(first);
            if (l1 == null) {
                l1 = new LinkedList<>();
                l1.add(first);
            }
            l1.add(second);
            l1.sort(new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return Integer.valueOf(o1) - Integer.valueOf(o2);
                }
            });
            oneCoTypes.add(first);
            oneCoTypes.add(second);
            partition1Map.put(first, l1);
        }
        List<String> partition1 = new LinkedList<>();
        for (List<String> values : partition1Map.values()) {
            StringBuffer sb = new StringBuffer();
            if (values.size() > 0) {
                for (String value : values) {
                    sb.append(value + ",");
                }
                sb.deleteCharAt(sb.length() - 1);
            }
            partition1.add(sb.toString());
        }
        return partition1;
    }

    /**
     * 分区2：用非频繁2阶模式对 所有候选长模式 拆分
     *
     * @param twoCoTypes     频繁2阶模式
     * @param twoCoreFeature 可能组成3阶模式的特征，用于生成所有2阶模式
     * @param partition1     分区1的结果
     */
    public static Set<String> partition2(Set<String> twoCoTypes, Set<String> twoCoreFeature, List<String> partition1) {
        long start = System.currentTimeMillis();
        //获取非频繁2阶模式
        String[] oneCoTypeArr = twoCoreFeature.toArray(new String[]{});
        Set<String> non2FrequentPattern = new HashSet<>();
        for (int i = 0; i < oneCoTypeArr.length - 1; i++) {
            String first = oneCoTypeArr[i];
            for (int j = i + 1; j < oneCoTypeArr.length; j++) {
                String newPattern = first + "," + oneCoTypeArr[j];
                if (!twoCoTypes.contains(newPattern)) {
                    non2FrequentPattern.add(newPattern);
                }
            }
        }
        System.out.print("=> 获取非频繁2阶模式完成！共 " + non2FrequentPattern.size()
                + "，耗时：" + (System.currentTimeMillis() - start) + "毫秒  ");
        //分区2
        int num = 0;

        Set<String> processSet = new HashSet<>();//已分区2过的模式集
        for (int i = 0; i < partition1.size(); i++) {
//            System.out.println("正在处理partion1。。" + i + "/" + partition1.size());
            boolean existNon2 = false;
            String now = partition1.get(i);
            String[] nowSplit = now.split(",");
            if (processSet.contains(now)) {
                partition1.remove(i);
                i--;
                continue;
            } else {
                processSet.add(now);
            }

            for (int j = 1; j < nowSplit.length; j++) { //因为partition2的第一个特征和其他特征组成的2阶模式都是频繁的，所以直接跳过
                if (existNon2) {
                    break;
                }
                String first = nowSplit[j];
                for (int k = j + 1; k < nowSplit.length; k++) {
                    num++;
                    String second = nowSplit[k];
                    if (non2FrequentPattern.contains(first + "," + second)) {
                        existNon2 = true;
                        partition1.remove(i);
                        if (now.contains("," + first + ",")) { //在中间或者末尾
                            partition1.add(i, now.replace("," + first + ",", ","));
                        } else {
                            partition1.add(i, now.substring(0, now.lastIndexOf("," + first)));
                        }
                        if (now.contains("," + second + ",")) {
                            partition1.add(i, now.replace("," + second + ",", ","));
                        } else {
                            partition1.add(i, now.substring(0, now.lastIndexOf("," + second)));
                        }
                        i--;
                        break;
                    }
                }
            }
        }

        Set<String> partition2 = new HashSet<>();
        CollectionUtil.addAll(partition2, partition1);
        System.out.println("partion2 和非频繁2阶模式 对比了" + num + "次");
        return partition2;
    }

    public static boolean isFrequent(String pattern, Map<String, List<Set<String>>> nCoPatternInstance) {
        if (Properties.considerExistNum) {
            return isFrequentByOrder(pattern, nCoPatternInstance);
        } else {
            if (nCoPatternInstance.containsKey(pattern)) //该pattern之前有计算过
            {
                if (nCoPatternInstance.get(pattern) == null) {
                    return false;
                }
                return true;
            }
            String[] patternSplit = pattern.split(",");
            int k = patternSplit.length;
            //一个k阶pattern由2个(k-1)阶+1个2阶组成   { 0~(k-2) 和 0~(k-3)+(k-1) 和 (k-2)+(k-3)}
            //通过末尾的2阶子模式进行拆解
            String basePattern = Stream.of(patternSplit).limit(k - 2).collect(Collectors.joining(","));
            String end2_feature1 = patternSplit[k - 2];
            String end2_feature2 = patternSplit[k - 1];
            String end2Pattern = end2_feature1 + "," + end2_feature2;
            String pattern1 = basePattern + "," + end2_feature1; //去掉最后一个特征
            String pattern2 = basePattern + "," + end2_feature2; //去掉倒数第二个特征
            if (nCoPatternInstance.get(pattern1) == null || nCoPatternInstance.get(pattern2) == null) { //子模式在递归中必定被计算过
                nCoPatternInstance.put(pattern, null);
                return false;
            }
            List<Set<String>> participateInstances = new LinkedList<>();
            //求basePattern中各个特征在pattern1和pattern2的交集，此为附加条件2
            String[] basePatternSplit = basePattern.split(",");
            for (int i = 0; i < basePatternSplit.length; i++) {
                String feature = basePatternSplit[i];
                Set<String> s1 = nCoPatternInstance.get(pattern1).get(i);
                Set<String> s2 = nCoPatternInstance.get(pattern2).get(i); //pattern2中feature的集合
                Set<String> intersect = Util.intersect(s1, s2);
                if ((double) intersect.size() / countPerFeature.get(feature) < Properties.support) //交集之后参与度小于阈值，则直接返回false
                {
                    nCoPatternInstance.put(pattern, null);
                    return false;
                }
                //否则加入到dic中作为备选
                participateInstances.add(intersect);
            }
            //判断附加条件1是否满足
            String frequent2 = end2Pattern;
            Set<String> s11 = nCoPatternInstance.get(pattern1).get(k - 2);
            Set<String> s12 = nCoPatternInstance.get(end2Pattern).get(0);
            Set<String> intersect11 = Util.intersect(s11, s12);
            if ((double) intersect11.size() / countPerFeature.get(end2_feature1) < Properties.support) {
                nCoPatternInstance.put(pattern, null);
                return false;
            }
            participateInstances.add(intersect11);

            Set<String> s21 = nCoPatternInstance.get(pattern2).get(k - 2);
            Set<String> s22 = nCoPatternInstance.get(end2Pattern).get(1);
            Set<String> intersect22 = Util.intersect(s21, s22);
            if ((double) intersect22.size() / countPerFeature.get(end2_feature2) < Properties.support) {
                nCoPatternInstance.put(pattern, null);
                return false;
            }
            participateInstances.add(intersect22);

            //如果能走到这里，说明此模式频繁，在频繁项实例集合中插入该频繁项
            nCoPatternInstance.put(pattern, participateInstances);
            return true;
        }
    }

    /**
     * 判断某个模式是否是频繁项，采用K-1阶判断的模式，这里类似于全连接（通过末尾的2阶子模式进行拆解）
     *
     * @param pattern            待判定模式
     * @param nCoPatternInstance 所有已计算模式及其参与实例
     * @return 模式是否频繁
     */
    public static boolean isFrequentByOrder(String pattern, Map<String, List<Set<String>>> nCoPatternInstance) {
        String patternOrder = Util.getOderString(pattern);
//        if (!patternOrder.equals(pattern)) {
//            System.out.println(1);
//        }
        //模式存储按字母顺序，拆分模式按出现次数降序
        if (nCoPatternInstance.containsKey(patternOrder)) //该patterOrder之前有计算过
        {
            if (nCoPatternInstance.get(patternOrder) == null) {
                return false;
            }
            return true;
        }

        String[] patternSplit = pattern.split(",");
        int k = patternSplit.length;
        //一个k阶pattern由2个(k-1)阶+1个2阶组成   { 0~(k-2) 和 0~(k-3)+(k-1) 和 (k-2)+(k-3)}
        //通过末尾的2阶子模式进行拆解
        String basePattern = Stream.of(patternSplit).limit(k - 2).collect(Collectors.joining(","));
        String end2_feature1 = patternSplit[k - 2];
        String end2_feature2 = patternSplit[k - 1];
        String end2Pattern = end2_feature1 + "," + end2_feature2;

        String pattern1 = basePattern + "," + end2_feature1; //去掉最后一个特征
        String pattern2 = basePattern + "," + end2_feature2; //去掉倒数第二个特征

        String pattern1Order = Util.getOderString(pattern1);
        String[] pattern1OrderSplit = Util.getOderString(pattern1).split(",");
        String pattern2Order = Util.getOderString(pattern2);
        String[] pattern2OrderSplit = Util.getOderString(pattern2).split(",");
        String end2PatternOrder = Util.getOderString(end2Pattern);
        String[] end2PatternOrderSplit = Util.getOderString(end2Pattern).split(",");


        List<Set<String>> pattern1Instance = nCoPatternInstance.get(pattern1Order);
        List<Set<String>> pattern2Instance = nCoPatternInstance.get(pattern2Order);
        List<Set<String>> end2patternInstance = nCoPatternInstance.get(end2PatternOrder);
        if (pattern1Instance == null || pattern2Instance == null) { //子模式在递归中必定被计算过
            nCoPatternInstance.put(patternOrder, null);
            return false;
        }
        List<Set<String>> participateInstances = new LinkedList<>();
        //求basePattern中各个特征在pattern1和pattern2的交集，此为附加条件2
        String[] basePatternSplit = basePattern.split(",");
        for (int i = 0; i < basePatternSplit.length; i++) {
            String feature = basePatternSplit[i];

            Set<String> s1 = pattern1Instance.get(Util.getIndex(pattern1OrderSplit, feature));
            Set<String> s2 = pattern2Instance.get(Util.getIndex(pattern2OrderSplit, feature)); //pattern2中feature的集合
            Set<String> intersect = Util.intersect(s1, s2);
            if ((double) intersect.size() / countPerFeature.get(feature) < Properties.support) //交集之后参与度小于阈值，则直接返回false
            {
                nCoPatternInstance.put(patternOrder, null);
                return false;
            }
            //否则加入到dic中作为备选
            participateInstances.add(intersect);
        }
        //判断附加条件1是否满足
        Set<String> s11 = null;
        Set<String> s12 = end2patternInstance.get(Util.getIndex(end2PatternOrderSplit, end2_feature1));
        try {
            s11 = pattern1Instance.get(Util.getIndex(pattern1OrderSplit, end2_feature1));
        } catch (Exception e) {
            System.out.println(1);
        }
        Set<String> intersect11 = Util.intersect(s11, s12);
        if ((double) intersect11.size() / countPerFeature.get(end2_feature1) < Properties.support) {
            nCoPatternInstance.put(patternOrder, null);
            return false;
        }
        participateInstances.add(intersect11);

        Set<String> s21 = pattern2Instance.get(Util.getIndex(pattern2OrderSplit, end2_feature2));
        Set<String> s22 = end2patternInstance.get(Util.getIndex(end2PatternOrderSplit, end2_feature2));
        Set<String> intersect22 = Util.intersect(s21, s22);
        if ((double) intersect22.size() / countPerFeature.get(end2_feature2) < Properties.support) {
            nCoPatternInstance.put(patternOrder, null);
            return false;
        }
        participateInstances.add(intersect22);

        //如果能走到这里，说明此模式频繁，在频繁项实例集合中插入该频繁项
        nCoPatternInstance.put(patternOrder, participateInstances);
        return true;
    }

    /**
     * @param candidatePattern   候选模式字符串
     * @param nCoPatternInstance 频繁模式及参与实例
     * @param maxPattern         最大频繁模式
     * @param candidateMap       所有候选模式。当2个子模式不符合，将其他子模式加入候选
     */
    public static void CPDByLevel(String candidatePattern, Map<String, List<Set<String>>> nCoPatternInstance,
                                  Map<Integer, Set<String>> maxPattern, Map<Integer, Set<String>> candidateMap) {

        String candidatePatternOder = candidatePattern;
        if (Properties.considerExistNum) {
            candidatePatternOder = Util.getOderString(candidatePattern);
        }
        if (nCoPatternInstance.containsKey(candidatePatternOder)) //如果已经处理过该模式，返回
        {
            return;
        }
        String[] patternSplit = candidatePattern.split(",");
        int k = patternSplit.length;


        String basePattern = Stream.of(patternSplit).limit(k - 2).collect(Collectors.joining(","));
        String end2_feature1 = patternSplit[k - 2];
        String end2_feature2 = patternSplit[k - 1];
        String end2Pattern = end2_feature1 + "," + end2_feature2;

        String pattern1 = basePattern + "," + end2_feature1; //去掉最后一个特征
        String pattern2 = basePattern + "," + end2_feature2; //去掉倒数第二个特征

        Set<String> tempLevelPatternSet = maxPattern.computeIfAbsent(k, m -> new HashSet<>());
        //当前模式不频繁时，将其他k-1阶子模式加入候选
        Set<String> lowLevelCandidateSet = candidateMap.computeIfAbsent(k - 1, m -> new HashSet<>());

        if (k == 3) {
            if (isFrequent(candidatePattern, nCoPatternInstance)) {
                tempLevelPatternSet.add(candidatePattern);
            }
        } else {
            CPDByLevel(pattern1, nCoPatternInstance, maxPattern, candidateMap);  //取pattern1递归
            CPDByLevel(pattern2, nCoPatternInstance, maxPattern, candidateMap);  //取pattern2递归
            //pattern1和pattern2 k-1阶子模式都是频繁项, 但当前k阶模式不是，搜索其他k-1子模式
            if (isFrequent(candidatePattern, nCoPatternInstance)) {
                tempLevelPatternSet.add(candidatePattern);
            } else {
                //生成candidate的其他低一阶的子模式，加入候选模式中
//                if (k == 5){
////                    System.out.println(1);
//                }
                for (int i = 0; i < k - 2; i++) //去末尾的2个，k阶可以产生k-2个
                {
                    int finalI = i;
                    String newPattern = Arrays.stream(patternSplit).filter(
                            kk -> !kk.equals(patternSplit[finalI])).collect(Collectors.joining(","));

                    lowLevelCandidateSet.add(newPattern);
                }
            }
        }
    }

    /**
     * 通过末尾的2阶子模式进行拆解
     *
     * @param candidatePattern   待递归模式
     * @param nCoPatternInstance 所有已计算模式及其参与实例
     * @param maxPreCandidates
     */
    public static void CPD(String candidatePattern, Map<String, List<Set<String>>> nCoPatternInstance,
                           Set<String> maxPreCandidates, int[] candidateNum) {
        if (nCoPatternInstance.containsKey(candidatePattern)) //如果已经处理过该模式，返回
        {
            return;
        }
        String[] patternSplit = candidatePattern.split(",");
        int k = patternSplit.length;
        try {
            String basePattern2 = Stream.of(patternSplit).limit(k - 2).collect(Collectors.joining(","));
        } catch (Exception e) {
            System.out.println(1);
        }
        String basePattern = Stream.of(patternSplit).limit(k - 2).collect(Collectors.joining(","));
        String end2_feature1 = patternSplit[k - 2];
        String end2_feature2 = patternSplit[k - 1];
        String end2Pattern = end2_feature1 + "," + end2_feature2;
        String pattern1 = basePattern + "," + end2_feature1; //去掉最后一个特征
        String pattern2 = basePattern + "," + end2_feature2; //去掉倒数第二个特征


        if (k == 3) //长度为3阶的直接计算
        {
            if (isFrequent(candidatePattern, nCoPatternInstance)) {
                maxPreCandidates.add(candidatePattern);
            }
        } else  //大于3阶的情况
        {
            CPD(pattern1, nCoPatternInstance, maxPreCandidates, candidateNum);  //取pattern1递归
            CPD(pattern2, nCoPatternInstance, maxPreCandidates, candidateNum);  //取pattern2递归
            //pattern1和pattern2 k-1阶子模式都是频繁项, 但当前k阶模式不是，搜索其他k-1子模式
            if (isFrequent(candidatePattern, nCoPatternInstance)) {
                maxPreCandidates.add(candidatePattern);
            } else {
                //生成candidate的其他低一阶的子模式，并依此递归
                for (int i = 0; i < k - 2; i++) //去头和末尾的2个，k阶可以产生k-3个
                {
                    candidateNum[0]++;
                    int finalI = i;
                    String newPattern = Arrays.stream(patternSplit).filter(
                            kk -> !kk.equals(patternSplit[finalI])).collect(Collectors.joining(","));
                    CPD(newPattern, nCoPatternInstance, maxPreCandidates, candidateNum);
                }
            }
        }
    }

    public static Set<String> getMaxPattern(String[] candidates, Map<String, List<Set<String>>> nCoPatternInstance) throws Exception {
        Set<String> maxPattern = new HashSet<>();
        int[] candidateNum = {0};
        for (int i = 0; i < candidates.length; i++) {
            candidateNum[0]++;
            String currPattern = candidates[i];
            if (i % 100 == 0) {
                System.out.println(("正在处理【" + currPattern + "】,整体进度:" + (i + 1) + '/' + candidates.length));
            }
            if (currPattern.split(",").length == 2) {
                maxPattern.add(currPattern);//SetFrequentText("2阶项【" + currPattern + "】直接进入极大候选！\n");
                continue;
            } else if (currPattern.split(",").length < 2) {
                throw new Exception("模式低于2阶！！！");
            }
            CPD(currPattern, nCoPatternInstance, maxPattern, candidateNum);
        }
        System.out.println("处理候选no level完成！共处理 " + candidateNum[0] + "个");
        return maxPattern;
    }

    public static Set<String> getMaxPatternByLevel(String[] candidates, Map<String, List<Set<String>>> nCoPatternInstance,
                                                   Map<String, Integer> corenessesMap) {
        Map<Integer, Set<String>> maxPattern = new HashMap<>();
        Map<Integer, Set<String>> candidateMap = new HashMap<>();
        int maxJie = 0;
        //将候选模式放入对应的n阶队列
        for (int i = 0; i < candidates.length; i++) {
            String currPattern = candidates[i];
            String[] currPatternSplit = currPattern.split(",");
            int currPatternJie = currPatternSplit.length;
            if (currPatternSplit.length >= 23) {
                Integer[] a = new Integer[currPatternJie];
                for (int j = 0; j < currPatternJie; j++) {
                    a[j] = corenessesMap.get(currPatternSplit[j]);
                }
            }
            //将模式按core从大到小排列
//            String patternOrderByCore = Arrays.stream(currPatternSplit).sorted(new Comparator<String>() {
//                @Override
//                public int compare(String o1, String o2) {
//                    return corenessesMap.get(o2).compareTo(corenessesMap.get(o1));
//                }
//            }).collect(Collectors.joining(","));
//            if (Properties.consider2Core) {
//                int minCore = 100;
//                List<Integer> coreArr = new LinkedList<>();
//                for (int j = 0; j < currPatternJie; j++) {
//                    String feature = currPatternSplit[j];
//                    int core = corenessesMap.get(feature);
//                    if (core < minCore) {
//                        minCore = core;
//                    }
//                    coreArr.add(core);
//                }
//                if (minCore<currPatternJie-1){
//                    currPatternJie = Util.getMaxSupportCore(coreArr);
//                }
//            }
            if (currPatternJie > maxJie) {
                maxJie = currPatternJie;
            }
            Set<String> candidateList = candidateMap.computeIfAbsent(currPatternJie, k -> new HashSet<>());
//            candidateList.add(currPattern);
            candidateList.add(currPattern);
            //TODO 前缀树代替candidateList
        }
        int candidateNum = 0;
        for (int i = maxJie; i > 2; i--) {
            long start = System.currentTimeMillis();
            Set<String> candidateList = candidateMap.get(i);
            //对每阶候选模式统计出现次数，按次数排序
            Map<String, Integer> timeMap = new HashMap<>();
            candidateList.stream().forEach(
                    pattern -> Stream.of(pattern.split(",")).forEach(
                            f -> timeMap.put(
                                    f, timeMap.get(f) == null ? 1 : timeMap.get(f) + 1)));

            if (Properties.considerExistNum) {
                Set<String> candidateListOrderByNum = candidateList.stream().map(
                        pattern -> Stream.of(pattern.split(",")).sorted(new Comparator<String>() {
                            @Override
                            public int compare(String o1, String o2) {
                                if (timeMap.get(o2) == timeMap.get(o1)) {
                                    return Integer.valueOf(o1).compareTo(Integer.valueOf(o2));
                                } else {
                                    return timeMap.get(o1).compareTo(timeMap.get(o2));
                                }
                            }
                        }).collect(Collectors.joining(","))).collect(Collectors.toSet());
                candidateList = candidateListOrderByNum;
            }
            int j = 0;
            for (String currPattern : candidateList) {
                CPDByLevel(currPattern, nCoPatternInstance, maxPattern, candidateMap);
                j++;
                if (j % 1000 == 0) {
                }
//                    System.out.println(i + "阶进度：" + j + "/" + candidateList.size());
            }

            candidateNum += candidateList.size();
            System.out.println("处理" + i + "阶候选模式完成，共 " + candidateList.size() + "个，已处理 " + candidateNum + "个候选模式,耗时: "
                    + (System.currentTimeMillis() - start) + "毫秒,下一阶模式数：" + candidateMap.get(i - 1).size());
            if (maxPattern.get(i) != null && maxPattern.get(i).size() > 0) {
                System.out.println("处理候选with level完成！共处理 " + candidateNum + "个");
                return maxPattern.get(i);
            }
        }
        System.err.println("遍历候选模式结束，不存在3阶以上模式！！1");
        return maxPattern.get(maxJie);
    }

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        //从保存的poi.txt获取poi数据集            如(b1:B) hashMap
        Map<String, String> poiTypeMap = preprocess();
        System.out.println("=> 读取数据集：poi点数：" + poiTypeMap.size() + " 原数据：" + Properties.maxNum
                + " 耗时 " + (System.currentTimeMillis() - start) + " 毫秒");
        start = System.currentTimeMillis();
        long allStart = start;
        System.out.println("==========================亚频繁模式===============================");
        //1.获取1阶co-location oneCoMap(key：特征 value：特征实例集)  如(A:a1,a2,a3)
        Map<String, List<String>> oneCoMap = gen1Co(poiTypeMap);
        System.out.println("===========1.获取1阶co-location耗时 " + (System.currentTimeMillis() - start) + " 毫秒" + getCoDetail(oneCoMap, 1));
        start = System.currentTimeMillis();

        //2. 2阶co-location模式（根据倒排星型邻居集）
        String twoCoMethod = "invert";
        Map<String, List<Set<String>>> sp_twoCoMap1 = new HashMap<>();
        Map<String, List<Set<String>>> sp_twoCoMap2 = new HashMap<>();
        if (twoCoMethod.equals("base") || twoCoMethod.equals("both")) {
            //星型邻居特征集（包括比自己特征小的）    如(b1: A,E,F) treeMap
            sp_twoCoMap1 = gen2CoBySpFile(poiTypeMap, oneCoMap);
        }
        if (twoCoMethod.equals("invert") || twoCoMethod.equals("both")) {
            sp_twoCoMap2 = gen2CoByInvertSpFile();
        }
        for (Map.Entry<String, List<Set<String>>> entry : sp_twoCoMap1.entrySet()) {
            String key = entry.getKey();
            List<Set<String>> setsList = entry.getValue();
            List<Set<String>> setsList2 = sp_twoCoMap2.get(key);
            if (setsList2 == null || setsList2.size() == 0) {
                System.out.println(1);
            }
            Set<String> set11 = setsList.get(0);
            Set<String> set12 = setsList2.get(0);
            Set<String> set21 = setsList.get(1);
            Set<String> set22 = setsList2.get(1);
            Set<String> set = new HashSet<>();
            if (set11.size() == 0 || set12.size() == 0 || set21.size() == 0 || set22.size() == 0) {
                System.out.println(1);
            }
            set.addAll(set11);
            set.removeAll(set12);
            if (set.size() != 0) {
                System.out.println(1);
            }
            set = new HashSet<>();
            set.addAll(set21);
            set.removeAll(set22);
            if (set.size() != 0) {
                System.out.println(1);
            }
        }
        Map<String, List<Set<String>>> sp_twoCoMap = sp_twoCoMap2;

        System.out.println("没有剪枝前：" + getSPCoDetail(sp_twoCoMap, 2));
        cut(sp_twoCoMap);
        System.out.println("===========2.获取2阶co-location+剪枝耗时 " + (System.currentTimeMillis() - start) + " 毫秒" + getSPCoDetail(sp_twoCoMap, 2));

        //计算每个特征的k-core数，获取只包含2-core及以上的特征
        Map<String, Integer> corenessesMap = new HashMap<>();
        Set<String> twoCoreFeature = Util.get2Core(sp_twoCoMap.keySet(), corenessesMap);
        if (twoCoreFeature.size() == 0) {
            System.err.println("不存在3阶模式！！！！");
            return;
        }
        Map<Integer, String> coreMap = new HashMap<>();
        for (Map.Entry<String, Integer> entry : corenessesMap.entrySet()) {
            Integer core = entry.getValue();
            String features = coreMap.computeIfAbsent(entry.getValue(), k -> new String());
            features += entry.getKey() + ",";
            coreMap.put(core, features);
        }


        long partitionStart = System.currentTimeMillis();
        long now = partitionStart;
        //分区1
        Set<String> oneCoTypes = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return Integer.valueOf(o1).compareTo(Integer.valueOf(o2));
            }
        });
        List<String> partition1 = partition1(sp_twoCoMap.keySet(), twoCoreFeature, Properties.consider2Core, oneCoTypes);
        System.out.println("=> 剪枝后特征数：" + oneCoTypes.size());
        if (twoCoreFeature.size() != oneCoTypes.size()) {
            System.err.println("异常！！");
        }
        System.out.println("=> 分区1完成！共 " + partition1.size() + ",耗时：" + (System.currentTimeMillis() - now) + " 毫秒");
        now = System.currentTimeMillis();

        //分区2
        Set<String> partition2 = null;
        if (Properties.consider2Core) { //
            partition2 = partition2(sp_twoCoMap.keySet(), twoCoreFeature, partition1);
        } else { //考虑k-core时，oneCoTypes为空
            partition2 = partition2(sp_twoCoMap.keySet(), oneCoTypes, partition1);
        }
        String[] candidates = partition2.toArray(new String[]{});
        System.out.println("=> 分区2完成！" + partition2.size() + ",耗时：" + (System.currentTimeMillis() - now) + " 毫秒");


        //处理候选模式
        Map<String, List<Set<String>>> nCoPatternInstance = sp_twoCoMap;//记录已计算的n阶模式及其参与实例
        Set<String> maxPattern = null;
        if (Properties.useLevelCandidate) { //逐层处理候选和k-core一起使用
            maxPattern = getMaxPatternByLevel(candidates, nCoPatternInstance, corenessesMap);
        } else {
            maxPattern = getMaxPattern(candidates, nCoPatternInstance);
        }

        //处理结果
        Map<Integer, List<String>> resultAll = new HashMap<>();
        int max = 0;
        for (String s : maxPattern) {
            String[] split = s.split(",");
            if (split.length > max) {
                max = split.length;
            }
            List<String> list = resultAll.computeIfAbsent(split.length, k -> new LinkedList<>());
            list.add(s);
        }
        long partitionEnd = System.currentTimeMillis();
        System.out.println("分区算法耗时: " + (partitionEnd - partitionStart) + "毫秒,结果：" + resultAll.get(max));
        System.out.println("算法总耗时: " + (partitionEnd - allStart) + "毫秒");
        System.out.println("取交集耗时 " + Util.costTime + "毫秒，共 " + Util.costNum);
        System.out.println("模式字符串排序耗时 " + Util.costOderStringTime + "毫秒，共 " + Util.costOderStringNum);

//        Map<String, List<List<String>>> result = new HashMap<>();
//        //3. n阶co-location模式
//        for (int i = 3; i <= Properties.maxJie + 100; i++) {
//            start = System.currentTimeMillis();
//            sp_nCoMap = genAllNPlusCo(sp_nCoMap, oneCoMap, neighborTypeMap, invertedNeighborMap, "invert");
//            if (sp_nCoMap.size() == 0) {
//                break;
//            } else {
//                result = sp_nCoMap;
//            }
//            System.out.println("没有剪枝前：" + getSPCoDetail(sp_nCoMap, i));
//            cut(sp_nCoMap);
//            System.out.println("===========" + i + ".获取" + i + "阶co-location耗时 " + (System.currentTimeMillis() - start) + " 毫秒"
//                    + getSPCoDetail(sp_nCoMap, i));
//        }
//
//        long nomalEnd = System.currentTimeMillis();
//
//        System.out.println("分区算法耗时: " + (partitionEnd - partitionStart) + "毫秒,结果：" + resultAll.get(max));
//        System.out.println("逐阶求取算法耗时: " + (nomalEnd - partitionEnd) + "毫秒,结果：" + result.keySet());

        System.out.print("[" + code2name(resultAll.get(max).toString()) + "]");
    }

}
